﻿using System;
using System.Globalization;
using System.Linq.Expressions;
using System.Reflection;
using Loki.Resources;

namespace Loki.Utils
{
    public static class ExpressionHelper
    {
        /// <summary>
        /// Gets the property represented by the lambda expression.
        /// </summary>
        /// <typeparam name="TTarget">The type of the target.</typeparam>
        /// <typeparam name="TProperty">The type of the property.</typeparam>
        /// <param name="P_Property">An expression that accesses a property.</param>
        /// <returns>The property info.</returns>
        public static PropertyInfo GetProperty<TTarget, TProperty>(Expression<Func<TTarget, TProperty>> P_Property)
        {
            return GetMember(P_Property) as PropertyInfo;
        }

        /// <summary>
        /// Gets the member represented by the lambda expression.
        /// </summary>
        /// <typeparam name="TTarget">The type of the target.</typeparam>
        /// <typeparam name="TProperty">The type of the property.</typeparam>
        /// <param name="P_Member">An expression that accesses a member.</param>
        /// <returns>The member info.</returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "Use derived type to force compile check")]
        public static MemberInfo GetMember<TTarget, TProperty>(Expression<Func<TTarget, TProperty>> P_Member)
        {
            MemberExpression L_MemberExpr = null;

            // The Func<TTarget, object> we use returns an object, so first statement can be either 
            // a cast (if the field/property does not return an object) or the direct member access.
            if (P_Member.Body.NodeType == ExpressionType.Convert)
            {
                // The cast is an unary expression, where the operand is the 
                // actual member access expression.
                L_MemberExpr = ((UnaryExpression)P_Member.Body).Operand as MemberExpression;
            }
            else if (P_Member.Body.NodeType == ExpressionType.MemberAccess)
            {
                L_MemberExpr = P_Member.Body as MemberExpression;
            }

            return L_MemberExpr.Member;
        }

        public static Expression<Func<TTarget>> New<TTarget>()
        {
            Type L_Type = typeof(TTarget);

            ConstructorInfo L_Builder = L_Type.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public, Type.DefaultBinder, Type.EmptyTypes, null);
            if (L_Builder == null)
            {
                throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, ErrorMessages.Utils_ExpressionHelper_NoConstructor, L_Type.FullName));
            }

            return Expression.Lambda<Func<TTarget>>(Expression.New(L_Builder));
        }

        public static bool HasDefaultConstructor(Type P_Type)
        {
            if (P_Type.IsPrimitive)
            {
                return true;
            }

            if (P_Type.GetConstructor(BindingFlags.Public | BindingFlags.Instance | BindingFlags.CreateInstance, null, new Type[0], null) != null)
            {
                return true;
            }

            return false;
        }
    }
}
